﻿using PmSoft.Core.Domain.Entities.Caching;
using System.Collections.Concurrent;
using System.Reflection;

namespace PmSoft.Cache.Abstractions.EntityCache;

/// <summary>
/// 实体元数据信息，描述实体的缓存相关特性
/// </summary>
public class CacheEntityMetadata : IEntityMetadata
{
	private static readonly ConcurrentDictionary<Type, CacheEntityMetadata> _metadatas = new();

	/// <summary>
	/// 构造函数，初始化实体元数据
	/// </summary>
	/// <param name="entityType">实体类型</param>
	/// <param name="enableDistributedCache">是否启用分布式缓存</param>
	public CacheEntityMetadata(Type entityType, bool enableDistributedCache)
	{
		Type = entityType ?? throw new ArgumentNullException(nameof(entityType));
		EnableDistributedCache = enableDistributedCache;
		Name = entityType.Name;

		if (entityType.GetCustomAttribute<CacheSettingAttribute>() is { } attribute)
		{
			EnableCache = attribute.EnableCache;
			Name = !string.IsNullOrEmpty(attribute.Name) ? attribute.Name : Name;

			CachingExpirationType = attribute.ExpirationPolicy switch
			{
				EntityCacheExpirationPolicies.Stable => CachingExpirationType.Stable,
				EntityCacheExpirationPolicies.Usual => CachingExpirationType.UsualSingleObject,
				_ => CachingExpirationType.SingleObject
			};

			if (!string.IsNullOrEmpty(attribute.PropertyNamesOfArea))
			{
				const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
				PropertiesOfArea = attribute.PropertyNamesOfArea
					.Split(',', StringSplitOptions.RemoveEmptyEntries)
					.Select(name => Type.GetProperty(name.Trim(), bindingFlags))
					.Where(property => property != null)
					.Select(property => property!.AsEntityPropertyInfo())
					.ToList();
			}
		}
		PropertiesOfArea ??= new List<EntityPropertyInfo>();
	}

	/// <summary>
	/// 实体类型
	/// </summary>
	public Type Type { get; }

	/// <summary>
	/// 实体名称，可由特性指定，默认使用类型名
	/// </summary>
	public string Name { get; }

	/// <summary>
	/// 是否启用缓存
	/// </summary>
	public bool EnableCache { get; }

	/// <summary>
	/// 是否启用分布式缓存
	/// </summary>
	public bool EnableDistributedCache { get; }

	/// <summary>
	/// 缓存过期类型，默认 SingleObject
	/// </summary>
	public CachingExpirationType CachingExpirationType { get; set; } = CachingExpirationType.SingleObject;

	/// <summary>
	/// 缓存分区的属性列表
	/// </summary>
	public List<EntityPropertyInfo> PropertiesOfArea { get; }

	/// <summary>
	/// 根据实体类型获取元数据（线程安全）
	/// </summary>
	/// <param name="type">实体类型</param>
	/// <param name="enableDistributedCache">是否启用分布式缓存</param>
	/// <returns>实体元数据实例</returns>
	public static CacheEntityMetadata ForType(Type type, bool enableDistributedCache)
	{
		if (type == null) throw new ArgumentNullException(nameof(type));
		return _metadatas.GetOrAdd(type, t => new CacheEntityMetadata(t, enableDistributedCache));
	}
}